package com.example.javaapiclient.utils;

import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import com.example.javaapiclient.annotations.ESDocument;
import com.example.javaapiclient.annotations.ESField;
import com.example.javaapiclient.entity.common.BulkResponseResult;
import com.example.javaapiclient.enums.ESFieldType;
import com.example.javaapiclient.exception.BizException;
import com.example.javaapiclient.strategy.properties.ParseFieldMappingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.lang.reflect.*;
import java.util.*;

@Component
@Slf4j
public class ElasticSearchHelpUtils<T> {

    public ESDocument getESDocumentByEntity(T t) throws BizException{
        ESDocument document = t.getClass().getAnnotation(ESDocument.class);
        if (EmptyUtil.isEmpty(document)) {
            throw new BizException(0,t.getClass().getSimpleName() + "无ESDocument注解");
        }
        return document;
    }

    public ESDocument getESDocumentByClass(Class<T> clazz) throws BizException{
        ESDocument document = clazz.getAnnotation(ESDocument.class);
        if (EmptyUtil.isEmpty(document)) {
            throw new BizException(0,clazz.getSimpleName() + "无ESDocument注解");
        }
        return document;
    }

    /**
     * 从类的注解中获取索引名和别名，优先使用别名
     * @param t
     * @return
     * @throws BizException
     */
    public String getIndexName(T t) throws BizException{
        ESDocument document = getESDocumentByEntity(t);
        String indexName = document.indexName();
        String indexAliasesName = document.indexAliasesName();
        if (EmptyUtil.isEmpty(indexName)&&EmptyUtil.isEmpty(indexAliasesName)){
            throw new BizException(0,t.getClass().getSimpleName()+"类 索引名和索引别名不能同时为空");
        }
        return EmptyUtil.isNotEmpty(indexAliasesName)?indexAliasesName:indexName;
    }

    /**
     * 从类的注解中获取索引名和别名，优先使用别名
     * @param clazz
     * @return
     * @throws BizException
     */
    public String getIndexName(Class<T> clazz) throws BizException{
        ESDocument document = getESDocumentByClass(clazz);
        String indexName = document.indexName();
        String indexAliasesName = document.indexAliasesName();
        if (EmptyUtil.isEmpty(indexName)&&EmptyUtil.isEmpty(indexAliasesName)){
            throw new BizException(0,clazz.getSimpleName()+"类 索引名和索引别名不能同时为空");
        }
        return EmptyUtil.isNotEmpty(indexAliasesName)?indexAliasesName:indexName;
    }

    /**
     * 从类的注解中获取索引名
     * @param t
     * @return
     * @throws BizException
     */
    public String getOnlyIndexName(T t) throws BizException{
        ESDocument document = getESDocumentByEntity(t);
        String indexName = document.indexName();
        if (EmptyUtil.isEmpty(indexName)){
            throw new BizException(0,t.getClass().getSimpleName()+"类 索引名不能为空");
        }
        return indexName;
    }

    /**
     * 从类的注解中获取索引名
     * @param clazz
     * @return
     * @throws BizException
     */
    public String getOnlyIndexName(Class<T> clazz) throws BizException{
        ESDocument document = getESDocumentByClass(clazz);
        String indexName = document.indexName();
        if (EmptyUtil.isEmpty(indexName)){
            throw new BizException(0,clazz.getSimpleName()+"类 索引名不能为空");
        }
        return indexName;
    }

    /**
     * 文档批量执行结果实体转换
     * @param bulkResponse
     * @return
     */
    public List<BulkResponseResult> convertBulkResponseResult(BulkResponse bulkResponse){
        if (EmptyUtil.isEmpty(bulkResponse)){
            return Collections.emptyList();
        }
        List<BulkResponseResult> resultList = new ArrayList<>();
        for (BulkResponseItem bulkResponseItem : bulkResponse.items()) {
            BulkResponseResult bulkResponseResult = new BulkResponseResult();
            bulkResponseResult.setOpType(bulkResponseItem.operationType());
            bulkResponseResult.setIndexName(bulkResponseItem.index());
            bulkResponseResult.setId(bulkResponseItem.id());
            bulkResponseResult.setResult(bulkResponseItem.result());
            bulkResponseResult.setFailedFlag(EmptyUtil.isNotEmpty(bulkResponseItem.error()));
            bulkResponseResult.setFailureMessage(EmptyUtil.isEmpty(bulkResponseItem.error())?"":bulkResponseItem.error().reason());
            bulkResponseResult.setStatus(bulkResponseItem.status());
            bulkResponseResult.setPrimaryTerm(bulkResponseItem.primaryTerm());
            bulkResponseResult.setSeqNo(bulkResponseItem.seqNo());
            bulkResponseResult.setVersion(bulkResponseItem.version());
            bulkResponseResult.setForcedRefresh(bulkResponseItem.forcedRefresh());

            resultList.add(bulkResponseResult);
        }
        return resultList;
    }

    /**
     * 实体转map
     * @return
     */
    public Map<String,Object> objectToMap(T t){

        Map<String,Object> map = new HashMap<>();

        if (EmptyUtil.isEmpty(t)){
            return map;
        }
        Field[] fields = t.getClass().getDeclaredFields();
        map.putAll(getFieldValue(fields,t));
        Field[] superFields = t.getClass().getSuperclass().getDeclaredFields();
        map.putAll(getFieldValue(superFields,t));
        return map;
    }

    private Map<String,Object> getFieldValue(Field[] fields,Object t){
        Map<String,Object> map = new HashMap<>();
        for (Field field:fields){
            field.setAccessible(true);
            ESField esField = field.getAnnotation(ESField.class);
            try {
                ////实体属性注解中没有指定mapping中的字段名则使用实体中的属性名
                String fieldModifier = Modifier.toString(field.getModifiers());
                if (fieldModifier.contains("static") || fieldModifier.contains("final")){
                    continue;
                }
                map.put(EmptyUtil.isEmpty(esField)||EmptyUtil.isEmpty(esField.name())?field.getName():esField.name(),getValueByField(field, t));
            } catch (Exception e) {
                log.info("实体转map 字段：{}的属性值出错,出错原因",field.getName(),e);
            }
        }
        return map;
    }

    /**
     * 获取字段值
     * @param field
     * @param t
     * @return
     * @throws IllegalAccessException
     */
    private Object getValueByField(Field field,Object t) throws Exception {
        ESField esField = field.getAnnotation(ESField.class);
        Class<?>[] childTypeClasses = esField.childType();
        Class<?> childTypeClass = null;
        if (childTypeClasses.length > 0){
            childTypeClass = childTypeClasses[0];
        }

        Class<?> type = field.getType();
        if (childTypeClass != null && type == childTypeClass){
            return getObjValueByField(field, t);
        }else if(type == List.class){
            return getListValueByField(field, t,childTypeClass);
        }else{
            return getSimpleValueByField(field, t);
        }
    }

    /**
     * 获取对象的值
     * @param field
     * @param t
     * @return
     * @throws IllegalAccessException
     */
    private Map<String,Object> getObjValueByField(Field field,Object t) throws IllegalAccessException{
        Object o = field.get(t);
        Field[] declaredFields = o.getClass().getDeclaredFields();
        return getFieldValue(declaredFields,o);
    }

    /**
     * 获取集合类型类型的值
     * @param field
     * @param t
     * @param childTypeClass 子类型
     * @return
     * @throws IllegalAccessException
     */
    private List<Object> getListValueByField(Field field,Object t,Class<?> childTypeClass) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method m = List.class.getDeclaredMethod("size");
        int size = (Integer) m.invoke(field.get(t));//调用list的size方法，得到list的长度
        List<Object> mapList = new ArrayList<>();
        for (int i = 0; i < size; i++) {//遍历list，调用get方法，获取list中的对象实例
            Method getMethod = List.class.getDeclaredMethod("get", int.class);
            getMethod.setAccessible(true);

            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                Class<?> clz = (Class) pt.getActualTypeArguments()[0];//得到对象list中实例的类型

                Object o = getMethod.invoke(field.get(t), i);
                if (childTypeClass != null && clz != null && childTypeClass == clz){//泛型是对象类型
                    Field[] declaredFields = o.getClass().getDeclaredFields();
                    mapList.add(getFieldValue(declaredFields,o));
                }else{//泛型不是对象类型直接获取值加到list中
                    mapList.add(o);
                }
            }
        }
        return mapList;
    }

    /**
     * 获取简单类型的值
     * @param field
     * @param t
     * @return
     * @throws IllegalAccessException
     */
    private Object getSimpleValueByField(Field field,Object t) throws IllegalAccessException{
        return field.get(t);
    }

    /**
     * 根据总条数和每页条数 计算总页数
     * @param totalSize 总条数
     * @param pageSize 每页条数
     * @return 总页数
     */
    public int getTotalPages(long totalSize, int pageSize) {
        return totalSize == 0L ? 0 : (Integer.parseInt((totalSize % Long.parseLong(pageSize + "") == 0 ? totalSize / Long.parseLong(pageSize + "") : totalSize / Long.parseLong(pageSize + "") + 1L) + ""));
    }

    /**
     * 转换实体的mapping
     * @return
     */
    public Map<String,Object> getMapping(Class<T> clazz){
        Map<String,Object> mapping = new HashMap<>();
        mapping.put("properties",getProperties(clazz));
//        mapping.put("dynamic_templates",getDynamicTemplates());
        return mapping;
    }

    /**
     * 动态模板配置
     * @return
     */
//    public List<Map<String,Object>> getDynamicTemplates(){
//        List<Map<String,Object>> templatesList = new ArrayList<>();
//        Stream.of("Double","KeyWord","Integer","Ip","Text","Date").forEach(type->{
//            DynamicTemplatesService dynamicTemplates = (DynamicTemplatesService)SpringContextUtil.getBean("dynamicTemplates"+type);
//            templatesList.add(dynamicTemplates.getDynamicTemplates());
//        });
//        return templatesList;
//    }

    /**
     * 转换实体的mapping
     * @return
     */
    public Map<String,Object> getProperties(Class<T> clazz){

        Map<String,Object> map = new HashMap<>();

        if (EmptyUtil.isEmpty(clazz)){
            return map;
        }
        map.putAll(handleEntityFieldList(clazz.getDeclaredFields()));
        map.putAll(handleEntityFieldList(clazz.getSuperclass().getDeclaredFields()));
        return map;
    }
    private static Map<String,Object> handleEntityFieldList(Field[] fields ){
        Map<String,Object> map = new HashMap<>();
        for (Field field:fields){
            field.setAccessible(true);
            String fieldModifier = Modifier.toString(field.getModifiers());
            if (fieldModifier.contains("static") || fieldModifier.contains("final")){
                continue;
            }
            map.putAll(handleEntityField(field));
        }
        return map;
    }

    private static Map<String,Object> handleEntityField(Field field){

        Map<String,Object> fieldMap = new HashMap<>();

        ESField esField = field.getAnnotation(ESField.class);
        //没有指定索引字段名使用属性名
        fieldMap.put(EmptyUtil.isEmpty(esField)||EmptyUtil.isEmpty(esField.name())?field.getName():esField.name(),getFieldByType(esField));
        return fieldMap;
    }

    /**
     * 使用策略模式，根据指定的类型解析对应字段的mapping
     * @param esField
     * @return
     */
    private static Map<String,Object> getFieldByType(ESField esField){
        String beanName = "parseFieldMapping"+(EmptyUtil.isEmpty(esField)? ESFieldType.Keyword.toString():esField.type().toString());
        ParseFieldMappingService financeInvoiceHandler = (ParseFieldMappingService)SpringContextUtil.getBean(beanName);
        return financeInvoiceHandler.getFieldMapping(esField);
    }

}
